﻿using gixoom.Dom.Block;
using gixoom.Initiator.WcfWsHttp;

namespace gixoom.Dom.Storage.Block
{
    internal class SourceBlockStorage: ISourceBlockStorage
    {
        private IInitiatorBlockStorage[] initiatorBlockStorages;
        private IBlockFacade blockFacade;

        /// <summary>
        /// Creates SourceBlockStorage object.
        /// </summary>
        /// <exception cref="System.ArgumentNullException"><i>blockFacade</i> is
        /// a null reference.</exception>
        public SourceBlockStorage(IBlockFacade blockFacade)
        {
            Ensure.ArgumentNotNull(blockFacade, "blockFacade");

            this.blockFacade = blockFacade;
        }

        /// <summary>
        /// Stores the specified block.
        /// </summary>
        /// <param name="block">the block</param>
        /// <returns>true if the block was successfully stored</returns>
        /// <exception cref="ArgumentNullException"><i>block</i> is a null reference.</exception>
        public bool StoreBlock(IBlock block)
        {
            Ensure.ArgumentNotNull(block, "block");
            
            var index = SelectIndex(block.Id.Score.GetBytes()[0], initiatorBlockStorages.LongLength);
            IInitiatorBlockStorage initiatorBlockStorage = initiatorBlockStorages[index];
            var blockBytes = blockFacade.SerializeBlock(block);
            var isSuccessfull = initiatorBlockStorage.StoreBlock(blockBytes);

            return isSuccessfull;
        }

        /// <summary>
        /// Retrieves the block by the specified block id.
        /// </summary>
        /// <param name="blockId">the block id</param>
        /// <returns>the block by the specified block id or null if the block cannot be retrieved</returns>
        /// <exception cref="ArgumentNullException"><i>blockId</i> is a null reference.</exception>
        public IBlock RetrieveBlock(IBlockId blockId)
        {
            Ensure.ArgumentNotNull(blockId, "blockId");

            var index = SelectIndex(blockId.Score.GetBytes()[0], initiatorBlockStorages.LongLength);
            IInitiatorBlockStorage initiatorBlockStorage = initiatorBlockStorages[index];
            var blockIdBytes = blockFacade.SerializeBlockId(blockId);
            var contentBytes = initiatorBlockStorage.RetrieveBlockContent(blockIdBytes);

            var block = blockFacade.CreateBlockFromContent(blockId.BlockType, contentBytes);

            if (!block.Id.Score.Equals(blockId.Score))
            {
                block = null;
            }

            return block;
        }

        /// <summary>
        /// Checks whether the block with the specified block id exists.
        /// </summary>
        /// <param name="blockId">the block id to check existence of the block for</param>
        /// <returns>true if the block exists</returns>
        /// <exception cref="System.ArgumentNullException"><i>blockId</i> is a null reference.</exception>
        public bool ExistsBlock(IBlockId blockId)
        {
            Ensure.ArgumentNotNull(blockId, "blockId");

            var index = SelectIndex(blockId.Score.GetBytes()[0], initiatorBlockStorages.LongLength);
            IInitiatorBlockStorage initiatorBlockStorage = initiatorBlockStorages[index];
            var blockIdBytes = blockFacade.SerializeBlockId(blockId);
            var exists = initiatorBlockStorage.ExistsBlock(blockIdBytes);

            return exists;
        }

        /// <summary>
        /// Sets the array of initiator block storages.
        /// </summary>
        /// <param name="initiatorBlockStorages">the array of initiator block storages</param>
        /// <exception cref="System.ArgumentNullException"><i>initiatorBlockStorages</i>
        /// is a null reference.</exception>
        /// <exception cref="System.ArgumentException"><i>initiatorBlockStorages</i>
        /// length is too short.</exception>
        public void SetInitiatorBlockStorages(IInitiatorBlockStorage[] initiatorBlockStorages)
        {
            Ensure.ArgumentNotNull(initiatorBlockStorages, "initiatorBlockStorages");
            Ensure.EnoughItems(initiatorBlockStorages, 1, "initiatorBlockStorages");

            this.initiatorBlockStorages = initiatorBlockStorages;
        }

        private long SelectIndex(long criteria, long count)
        {
            long index = criteria % count;
            return index;
        }
    }
}
